﻿/*

20110106
avm1Loader supported.
rewrite class.

NOTE:
cache.loadSWF(_url:String)				** use to load swf
----------
dispatchEvent:
	LoadEvent
Util tools:
	cache.inUse								** return False when loading
	cache.SWF[url]							** return MovieClip, for display
	cache.releaseRAM						** delete ImageHolder, should be CPU resocurcse.

//Create the loader
// var loader:cacheSWF = new cacheSWF();
*/
package com.cf.core.Utils{
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.events.IOErrorEvent;
	import flash.events.EventDispatcher;
	import flash.events.SecurityErrorEvent;
	import flash.display.*;
	import flash.display.MovieClip;
	import flash.display.Loader;
	import flash.display.AVM1Movie;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLStream;
	import flash.net.LocalConnection;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	import flash.errors.EOFError;
	import com.cf.core.events.LoadEvent;
	public class cacheSWF extends EventDispatcher{// EventDispatcher{
		private var __inUse:Boolean=false;
		public function get inUse():Boolean{return __inUse}
		public function set inUse(_f:Boolean):void{__inUse=_f}
		private var G_currURL:String;		// record the target's url
		private var G_loader:Loader;
		private var G_stream:URLStream;
		
		public function loadSWF(_url:String):void {
			if(!inUse){
				inUse=true;
				G_currURL = _url;
				G_loader = new Loader();
				// we put the date in to URLStream to download
				// and.....
				G_stream = new URLStream();
				G_stream.addEventListener(IOErrorEvent.IO_ERROR, handleIoError);
				G_stream.addEventListener(ProgressEvent.PROGRESS, handlerProgress);
				G_stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
				G_stream.addEventListener(Event.COMPLETE, handleFileLoaded);
				G_stream.load(new URLRequest(G_currURL));
				dispatchEvent(new LoadEvent(LoadEvent.BEFORELOAD, {url: G_currURL}));
			}else{
				trace("cacheSWF : I'm working on ["+G_currURL+"] Loading["+_url+"] fail");
				dispatchEvent(new LoadEvent(LoadEvent.FAIL, {url: G_currURL}));
			}//if
		}//fn
		
		private function handlerProgress(e:ProgressEvent):void{
			dispatchEvent(new LoadEvent(LoadEvent.ONPROGRESS, e));
		}//fn
		private function handleIoError(e:IOErrorEvent):void{
			trace("cacheSWF URL : "+G_currURL+" didn't exist");
			G_loader.unload();
			G_stream.close();
			dispatchEvent(new LoadEvent(LoadEvent.FAIL, {url: G_currURL}));
		}//fn
		private function handleSecurityError(e:SecurityErrorEvent):void{
			dispatchEvent(new LoadEvent(LoadEvent.FAIL, {url: G_currURL}));
		}//fn

		private function handleFileLoaded(e:Event):void{
			G_stream.removeEventListener(IOErrorEvent.IO_ERROR, handleIoError);
			G_stream.removeEventListener(ProgressEvent.PROGRESS, handlerProgress);
			G_stream.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
			G_stream.removeEventListener(Event.COMPLETE, handleFileLoaded);
			// We download the whole file
			// and we need to know the file's info.
			var inputBytes:ByteArray = new ByteArray();
			G_stream.readBytes(inputBytes);
			G_stream.close();
			inputBytes.endian = Endian.LITTLE_ENDIAN;
			if (isCompressed(inputBytes)) {
				uncompress(inputBytes);
			}//if
			var version:uint = uint(inputBytes[3]);
			if (version < 9) { updateVersion(inputBytes, 9); }
			if (version > 7) {
				flagSWF9Bit(inputBytes);
			} else {
				insertFileAttributesTag(inputBytes);
			}//if
			// we did our job, let's press it to the loader.
			G_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleSwfLoaded);
			G_loader.loadBytes(inputBytes);
		}//fn
		private function handleSwfLoaded(e:Event):void{
			// and tell everyone we can use this function again.
			trace("cacheSWF URL : "+G_currURL+" ONLOAD as "+ this.SWF);
			inUse=false;
			G_loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, handleSwfLoaded);
			dispatchEvent(new LoadEvent(LoadEvent.ONLOAD, {url: G_currURL}));
		}//fn
		//////////////////////////////
		// AVM1 exchange core.
		//////////////////////////////
		private function isCompressed(bytes:ByteArray):Boolean{
			return bytes[0] == 0x43;
		}//fn
		private function uncompress(bytes:ByteArray):void{
			var cBytes:ByteArray = new ByteArray();
			cBytes.writeBytes(bytes, 8);
			bytes.length = 8;
			bytes.position = 8;
			cBytes.uncompress();
			bytes.writeBytes(cBytes);
			bytes[0] = 0x46;
			cBytes.length = 0;
		}//fn
		private function getBodyPosition(bytes:ByteArray):uint{
			var result:uint = 0;
			
			result += 3; // FWS/CWS
			result += 1; // version(byte)
			result += 4; // length(32bit-uint)
			
			var rectNBits:uint = bytes[result] >>> 3;
			result += (5 + rectNBits * 4) / 8; // stage(rect)
			
			result += 2;
			
			result += 1; // frameRate(byte)
			result += 2; // totalFrames(16bit-uint)
			
			return result;
		}//fn
		
		private function findFileAttributesPosition(offset:uint, bytes:ByteArray):uint{
			bytes.position = offset;
			
			try {
				for (;;) {
					var byte:uint = bytes.readShort();
					var tag:uint = byte >>> 6;
					if (tag == 69) {
						return bytes.position - 2;
					}//if
					var length:uint = byte & 0x3f;
					if (length == 0x3f) {
						length = bytes.readInt();
					}//if
					bytes.position += length;
				}//for
			}catch (e:EOFError) {}
			return NaN;
		}//fn
		private function flagSWF9Bit(bytes:ByteArray):void{
			var pos:uint = findFileAttributesPosition(getBodyPosition(bytes), bytes);
			if (!isNaN(pos)) {
				bytes[pos + 2] |= 0x08;
			}//if
		}//fn
		private function insertFileAttributesTag(bytes:ByteArray):void{
			var pos:uint = getBodyPosition(bytes);
			var afterBytes:ByteArray = new ByteArray();
			afterBytes.writeBytes(bytes, pos);
			bytes.length = pos;
			bytes.position = pos;
			bytes.writeByte(0x44);
			bytes.writeByte(0x11);
			bytes.writeByte(0x08);
			bytes.writeByte(0x00);
			bytes.writeByte(0x00);
			bytes.writeByte(0x00);
			bytes.writeBytes(afterBytes);
			afterBytes.length = 0;
		}//fn
		private function updateVersion(bytes:ByteArray, version:uint):void{
			bytes[3] = version;
		}//fn
		//////////////////////////////
		// I/O port
		//////////////////////////////
		public function get SWF():*{
			return G_loader.content;
		}//fn
		public function RELEASE():Boolean{
			if(!inUse){
				G_currURL="";
				G_loader.unload();
				try{G_stream.close();}catch(e){}
				dispatchEvent(new LoadEvent(LoadEvent.RELEASE, {url: G_currURL}));
				return true;
			}else{
				trace("cacheSWF can't not releaseRAM ["+G_currURL+"] ,still working");
				return false;
			}//fn
		}//fn
	}//class
}//package